`include "defines.v"
module lsu(
  input clk,
  input rst_n,
  //用户接口 
  input  valid_i,
  output ready_o,
  input  [`OP_W-1:0] op,
  input  [`XLEN-1:0] op1,
  input  [`XLEN-1:0] op2,
  input  [`XLEN-1:0] imm,
  output [`XLEN-1:0] out,
  // 访存接口
  output                 lsu_dcache_addr_valid_o ,
  output  [`PADDR_W-1:0] lsu_dcache_addr_o       ,
  output  [7:0]          lsu_dcache_strb_o       ,
  output                 lsu_dcache_wen_o        ,
  output                 lsu_dcache_invalidate_o ,
  output  [63:0]         lsu_dcache_wdata_o      ,
  input   [63:0]         dcache_lsu_rdata_i      ,
  input                  dcache_lsu_data_valid_i ,
  // 预留的MMU接口  
  output                 lsu_exu_trans_valid_o,
  input                  exu_lsu_trans_ready_i,
  output [`VADDR_W-1:0]  lsu_exu_trans_vaddr_o,
  input  [`PADDR_W-1:0]  exu_lsu_trans_paddr_i,
  // csr_ctrl_message
  input                   clintEn,
  output                  clintInterrupt,
  output                  debug_selClint  
);
wire invalidate = (op == `LSU_FEI) && valid_i;
reg  [`PADDR_W-1:0] lsu_paddr;
wire [`VADDR_W-1:0] lsu_vaddr = op1 + imm;
wire wen = valid_i &&  op[3] ;
wire ren = valid_i && ~op[3] ; 
wire [`XLEN-1:0] out_;
wire [`XLEN-1:0] clintRdata;
wire selClint = ( exu_lsu_trans_paddr_i[31:16] == 16'h0200);
assign debug_selClint = (lsu_paddr[31:16] == 16'h0200) && valid_i;
MemProxy proxy(
  .io_miniaddr  (lsu_paddr[2:0]), 
  .io_in_wmask  (op[2:0]), 
  .io_in_wdata  (op2), 
  .io_in_rdata  (out_), 
  .io_out_wmask (lsu_dcache_strb_o), 
  .io_out_wdata (lsu_dcache_wdata_o),
  .io_out_rdata (selClint ? clintRdata : dcache_lsu_rdata_i)
);
reg [`XLEN-1:0] memSave;
always@(posedge clk)
  if(dcache_lsu_data_valid_i)
    memSave <= out_;
  
assign out = selClint ? out_ : memSave;
parameter IDLE  = 2'b00,
          TRANS = 2'b01,
          REQ   = 2'b10,
          RESP  = 2'b11; 

reg [1:0] cur_state;
reg [1:0] nxt_state;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    cur_state <= IDLE;
  else 
    cur_state <= nxt_state ;

always@(*)begin
  case(cur_state)
    IDLE:if (invalidate) nxt_state=REQ; else if(wen || ren) nxt_state = TRANS;else nxt_state=IDLE;
    TRANS:if(exu_lsu_trans_ready_i && selClint) nxt_state = RESP; 
          else if (exu_lsu_trans_ready_i) nxt_state = REQ; else nxt_state = TRANS;
    REQ: if(dcache_lsu_data_valid_i) nxt_state=RESP;else nxt_state=REQ;
    RESP:nxt_state = IDLE;
    default:nxt_state = IDLE;
  endcase
end
// TO mmu
assign lsu_exu_trans_valid_o = (cur_state == TRANS);
assign lsu_exu_trans_vaddr_o = lsu_vaddr;
always@(posedge clk )
  if(exu_lsu_trans_ready_i && lsu_exu_trans_valid_o)
    lsu_paddr <= exu_lsu_trans_paddr_i;
// TO mem
assign lsu_dcache_addr_valid_o = (cur_state == REQ);
assign lsu_dcache_wen_o        = wen;
assign lsu_dcache_invalidate_o = invalidate;
assign lsu_dcache_addr_o       = lsu_paddr;
assign ready_o                 = (cur_state == RESP);

// reg [`XLEN-1:0] timee;
// always@(posedge clk or negedge rst_n)
//   if(~rst_n)
//     timee <= `ZERO;
//   else 
//     timee <= timee + 1'b1;
// always@(posedge clk )
//   if(lsu_dcache_addr_valid_o && dcache_lsu_data_valid_i && (lsu_paddr[31:4] == 28'h801d46b))
//     $fwrite(32'h8000_0001,"[LSU] wen %d %d\n",lsu_dcache_wen_o,timee);

// TO clint
clint Clint(
  .clk   (clk),
  .rst_n (rst_n),
  .wen   (selClint && wen),
  .addr  (lsu_paddr),
  .wdata (lsu_dcache_wdata_o),
  .rdata (clintRdata),
  // csr_ctrl_message
  .clintEn(clintEn),
  .clintInterrupt(clintInterrupt)
);
endmodule

module MemProxy(
  input  [2:0]  io_miniaddr, 
  input  [2:0]  io_in_wmask, 
  input  [63:0] io_in_wdata, 
  output [63:0] io_in_rdata, 
  output [7:0]  io_out_wmask, 
  output [63:0] io_out_wdata,
  input  [63:0] io_out_rdata
);
  wire [7:0] _mskb_T_1 = 3'h1 == io_miniaddr ? 8'h2 : 8'h1; // @[Mux.scala 80:57]
  wire [7:0] _mskb_T_3 = 3'h2 == io_miniaddr ? 8'h4 : _mskb_T_1; // @[Mux.scala 80:57]
  wire [7:0] _mskb_T_5 = 3'h3 == io_miniaddr ? 8'h8 : _mskb_T_3; // @[Mux.scala 80:57]
  wire [7:0] _mskb_T_7 = 3'h4 == io_miniaddr ? 8'h10 : _mskb_T_5; // @[Mux.scala 80:57]
  wire [7:0] _mskb_T_9 = 3'h5 == io_miniaddr ? 8'h20 : _mskb_T_7; // @[Mux.scala 80:57]
  wire [7:0] _mskb_T_11 = 3'h6 == io_miniaddr ? 8'h40 : _mskb_T_9; // @[Mux.scala 80:57]
  wire [7:0] mskb = 3'h7 == io_miniaddr ? 8'h80 : _mskb_T_11; // @[Mux.scala 80:57]
  wire [7:0] wdatb_lo = io_in_wdata[7:0]; // @[Memproxy.scala 34:39]
  wire [63:0] _wdatb_T = {56'h0,wdatb_lo}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_1 = {48'h0,wdatb_lo,8'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_2 = {40'h0,wdatb_lo,16'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_3 = {32'h0,wdatb_lo,24'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_4 = {24'h0,wdatb_lo,32'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_5 = {16'h0,wdatb_lo,40'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_6 = {8'h0,wdatb_lo,48'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_7 = {wdatb_lo,56'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatb_T_9 = 3'h1 == io_miniaddr ? _wdatb_T_1 : _wdatb_T; // @[Mux.scala 80:57]
  wire [63:0] _wdatb_T_11 = 3'h2 == io_miniaddr ? _wdatb_T_2 : _wdatb_T_9; // @[Mux.scala 80:57]
  wire [63:0] _wdatb_T_13 = 3'h3 == io_miniaddr ? _wdatb_T_3 : _wdatb_T_11; // @[Mux.scala 80:57]
  wire [63:0] _wdatb_T_15 = 3'h4 == io_miniaddr ? _wdatb_T_4 : _wdatb_T_13; // @[Mux.scala 80:57]
  wire [63:0] _wdatb_T_17 = 3'h5 == io_miniaddr ? _wdatb_T_5 : _wdatb_T_15; // @[Mux.scala 80:57]
  wire [63:0] _wdatb_T_19 = 3'h6 == io_miniaddr ? _wdatb_T_6 : _wdatb_T_17; // @[Mux.scala 80:57]
  wire [63:0] wdatb = 3'h7 == io_miniaddr ? _wdatb_T_7 : _wdatb_T_19; // @[Mux.scala 80:57]
  wire [7:0] _mskh_T_2 = 2'h1 == io_miniaddr[2:1] ? 8'hc : 8'h3; // @[Mux.scala 80:57]
  wire [7:0] _mskh_T_4 = 2'h2 == io_miniaddr[2:1] ? 8'h30 : _mskh_T_2; // @[Mux.scala 80:57]
  wire [7:0] mskh = 2'h3 == io_miniaddr[2:1] ? 8'hc0 : _mskh_T_4; // @[Mux.scala 80:57]
  wire [15:0] wdath_lo = io_in_wdata[15:0]; // @[Memproxy.scala 53:39]
  wire [63:0] _wdath_T_1 = {48'h0,wdath_lo}; // @[Cat.scala 30:58]
  wire [63:0] _wdath_T_2 = {32'h0,wdath_lo,16'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdath_T_3 = {16'h0,wdath_lo,32'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdath_T_4 = {wdath_lo,48'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdath_T_6 = 2'h1 == io_miniaddr[2:1] ? _wdath_T_2 : _wdath_T_1; // @[Mux.scala 80:57]
  wire [63:0] _wdath_T_8 = 2'h2 == io_miniaddr[2:1] ? _wdath_T_3 : _wdath_T_6; // @[Mux.scala 80:57]
  wire [63:0] wdath = 2'h3 == io_miniaddr[2:1] ? _wdath_T_4 : _wdath_T_8; // @[Mux.scala 80:57]
  wire [7:0] mskw = io_miniaddr[2] ? 8'hf0 : 8'hf; // @[Memproxy.scala 60:20]
  wire [31:0] wdatw_hi = io_in_wdata[31:0]; // @[Memproxy.scala 61:44]
  wire [63:0] _wdatw_T_1 = {wdatw_hi,32'h0}; // @[Cat.scala 30:58]
  wire [63:0] _wdatw_T_2 = {32'h0,wdatw_hi}; // @[Cat.scala 30:58]
  wire [63:0] wdatw = io_miniaddr[2] ? _wdatw_T_1 : _wdatw_T_2; // @[Memproxy.scala 61:20]
  wire  _io_out_wmask_T_1 = io_in_wmask[2:1] == 2'h0; // @[Memproxy.scala 64:27]
  wire  _io_out_wmask_T_3 = io_in_wmask[2:1] == 2'h1; // @[Memproxy.scala 65:27]
  wire  _io_out_wmask_T_5 = io_in_wmask[2:1] == 2'h2; // @[Memproxy.scala 66:27]
  wire  _io_out_wmask_T_7 = io_in_wmask[2:1] == 2'h3; // @[Memproxy.scala 67:27]
  wire [7:0] _io_out_wmask_T_8 = _io_out_wmask_T_7 ? 8'hff : 8'h0; // @[Mux.scala 98:16]
  wire [7:0] _io_out_wmask_T_9 = _io_out_wmask_T_5 ? mskw : _io_out_wmask_T_8; // @[Mux.scala 98:16]
  wire [7:0] _io_out_wmask_T_10 = _io_out_wmask_T_3 ? mskh : _io_out_wmask_T_9; // @[Mux.scala 98:16]
  wire [63:0] _io_out_wdata_T_8 = _io_out_wmask_T_7 ? io_in_wdata : 64'h0; // @[Mux.scala 98:16]
  wire [63:0] _io_out_wdata_T_9 = _io_out_wmask_T_5 ? wdatw : _io_out_wdata_T_8; // @[Mux.scala 98:16]
  wire [63:0] _io_out_wdata_T_10 = _io_out_wmask_T_3 ? wdath : _io_out_wdata_T_9; // @[Mux.scala 98:16]
  wire [7:0] _bsel_rd_T_9 = 3'h1 == io_miniaddr ? io_out_rdata[15:8] : io_out_rdata[7:0]; // @[Mux.scala 80:57]
  wire [7:0] _bsel_rd_T_11 = 3'h2 == io_miniaddr ? io_out_rdata[23:16] : _bsel_rd_T_9; // @[Mux.scala 80:57]
  wire [7:0] _bsel_rd_T_13 = 3'h3 == io_miniaddr ? io_out_rdata[31:24] : _bsel_rd_T_11; // @[Mux.scala 80:57]
  wire [7:0] _bsel_rd_T_15 = 3'h4 == io_miniaddr ? io_out_rdata[39:32] : _bsel_rd_T_13; // @[Mux.scala 80:57]
  wire [7:0] _bsel_rd_T_17 = 3'h5 == io_miniaddr ? io_out_rdata[47:40] : _bsel_rd_T_15; // @[Mux.scala 80:57]
  wire [7:0] _bsel_rd_T_19 = 3'h6 == io_miniaddr ? io_out_rdata[55:48] : _bsel_rd_T_17; // @[Mux.scala 80:57]
  wire [7:0] bsel_rd = 3'h7 == io_miniaddr ? io_out_rdata[63:56] : _bsel_rd_T_19; // @[Mux.scala 80:57]
  wire [15:0] _hsel_rd_T_6 = 2'h1 == io_miniaddr[2:1] ? io_out_rdata[31:16] : io_out_rdata[15:0]; // @[Mux.scala 80:57]
  wire [15:0] _hsel_rd_T_8 = 2'h2 == io_miniaddr[2:1] ? io_out_rdata[47:32] : _hsel_rd_T_6; // @[Mux.scala 80:57]
  wire [15:0] hsel_rd = 2'h3 == io_miniaddr[2:1] ? io_out_rdata[63:48] : _hsel_rd_T_8; // @[Mux.scala 80:57]
  wire [31:0] wsel_rd = io_miniaddr[2] ? io_out_rdata[63:32] : io_out_rdata[31:0]; // @[Memproxy.scala 98:23]
  wire  _io_in_rdata_T = io_in_wmask == 3'h0; // @[Memproxy.scala 101:22]
  wire [55:0] io_in_rdata_hi = bsel_rd[7] ? 56'hffffffffffffff : 56'h0; // @[Bitwise.scala 72:12]
  wire [63:0] _io_in_rdata_T_3 = {io_in_rdata_hi,bsel_rd}; // @[Cat.scala 30:58]
  wire  _io_in_rdata_T_4 = io_in_wmask == 3'h2; // @[Memproxy.scala 102:22]
  wire [47:0] io_in_rdata_hi_1 = hsel_rd[15] ? 48'hffffffffffff : 48'h0; // @[Bitwise.scala 72:12]
  wire [63:0] _io_in_rdata_T_7 = {io_in_rdata_hi_1,hsel_rd}; // @[Cat.scala 30:58]
  wire  _io_in_rdata_T_8 = io_in_wmask == 3'h4; // @[Memproxy.scala 103:22]
  wire [31:0] io_in_rdata_hi_2 = wsel_rd[31] ? 32'hffffffff : 32'h0; // @[Bitwise.scala 72:12]
  wire [63:0] _io_in_rdata_T_11 = {io_in_rdata_hi_2,wsel_rd}; // @[Cat.scala 30:58]
  wire  _io_in_rdata_T_12 = io_in_wmask == 3'h6; // @[Memproxy.scala 104:22]
  wire  _io_in_rdata_T_13 = io_in_wmask == 3'h1; // @[Memproxy.scala 105:22]
  wire [63:0] _io_in_rdata_T_14 = {56'h0,bsel_rd}; // @[Cat.scala 30:58]
  wire  _io_in_rdata_T_15 = io_in_wmask == 3'h3; // @[Memproxy.scala 106:22]
  wire [63:0] _io_in_rdata_T_16 = {48'h0,hsel_rd}; // @[Cat.scala 30:58]
  wire  _io_in_rdata_T_17 = io_in_wmask == 3'h5; // @[Memproxy.scala 107:22]
  wire [63:0] _io_in_rdata_T_18 = {32'h0,wsel_rd}; // @[Cat.scala 30:58]
  wire [63:0] _io_in_rdata_T_19 = _io_in_rdata_T_17 ? _io_in_rdata_T_18 : 64'h0; // @[Mux.scala 98:16]
  wire [63:0] _io_in_rdata_T_20 = _io_in_rdata_T_15 ? _io_in_rdata_T_16 : _io_in_rdata_T_19; // @[Mux.scala 98:16]
  wire [63:0] _io_in_rdata_T_21 = _io_in_rdata_T_13 ? _io_in_rdata_T_14 : _io_in_rdata_T_20; // @[Mux.scala 98:16]
  wire [63:0] _io_in_rdata_T_22 = _io_in_rdata_T_12 ? io_out_rdata : _io_in_rdata_T_21; // @[Mux.scala 98:16]
  wire [63:0] _io_in_rdata_T_23 = _io_in_rdata_T_8 ? _io_in_rdata_T_11 : _io_in_rdata_T_22; // @[Mux.scala 98:16]
  wire [63:0] _io_in_rdata_T_24 = _io_in_rdata_T_4 ? _io_in_rdata_T_7 : _io_in_rdata_T_23; // @[Mux.scala 98:16]
  assign io_in_rdata = _io_in_rdata_T ? _io_in_rdata_T_3 : _io_in_rdata_T_24; // @[Mux.scala 98:16]
  assign io_out_wmask = _io_out_wmask_T_1 ? mskb : _io_out_wmask_T_10; // @[Mux.scala 98:16]
  assign io_out_wdata = _io_out_wmask_T_1 ? wdatb : _io_out_wdata_T_10; // @[Mux.scala 98:16]
endmodule